package engine;

import engine.AbstractVectorTI.VectorType;
import engine.Tweet.Brand;
import engine.Tweet.Polarity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.SwingWorker;
import normalisation.Normalizer;
import ui.UserInterface;
import utilities.FileTools;
import utilities.MapTools;

public class Stats extends SwingWorker<Void, Integer> {

    private String trainPath;
    private UserInterface gui;//pour update la progression
    private ArrayList<String> vocabulary;//index id -> mot
    private TreeMap<String, Integer> wordID;//index mot -> id
    private Tweet[] tweets;
    private Normalizer normalizer;
    private TreeMap<String, Integer> corpusTfMap;
    private VectorType vectorType;
    //Basic Stats
    private Integer uniqueWordsCount;
    private Integer totalWordsCount;
    private Integer tweetsCount;
    private Integer avgWordCount;
    //Top/Frop & Bayes
    private TreeMap<String, Integer>[] tfMapsPolarity;//Tableau des tfMap pour chaque polarité
    private TreeMap<String, Integer>[] tfMapsBrand;//Tableau des tfMap pour chaque marque
    //Bayes
    Double[] aPriorisPolarity;
    Double[] aPriorisBrand;

    public Stats(String trainPath, Normalizer normalizer, VectorType vectorType) {
        this.trainPath = trainPath;
        this.normalizer = normalizer;
        this.vectorType = vectorType;
        this.gui = null;
    }

    public Tweet[] getTweets() {
        return tweets;
    }

    public Double[] getaPriorisPolarity() {
        return aPriorisPolarity;
    }

    public Double[] getaPriorisBrand() {
        return aPriorisBrand;
    }

    public TreeMap<String, Integer>[] getTfMapsPolarity() {
        return tfMapsPolarity;
    }

    public TreeMap<String, Integer>[] getTfMapsBrand() {
        return tfMapsBrand;
    }

    public ArrayList<String> getVocabulary() {
        return vocabulary;
    }

    public Integer getUniqueWordsCount() {
        return uniqueWordsCount;
    }

    public Integer getTotalWordsCount() {
        return totalWordsCount;
    }

    public Integer getTweetsCount() {
        return tweetsCount;
    }

    public Integer getAvgWordCount() {
        return avgWordCount;
    }

    public UserInterface getGui() {
        return gui;
    }

    public void setGui(UserInterface gui) {
        this.gui = gui;
    }

    public TreeMap<String, Integer> getCorpusTfMap() {
        return corpusTfMap;
    }

    public static Polarity parsePolarity(String polarityName) {
        for (Polarity p : Polarity.values()) {
            if (polarityName.equals(p.name())) {
                return p;
            }
        }

        return Polarity.unknown;
    }

    public static Brand parseBrand(String brandName) {
        for (Brand b : Brand.values()) {
            if (brandName.equals(b.name())) {
                return b;
            }
        }

        return Brand.unknown;
    }

    public void makeStats() throws IOException {
        //Stats
        uniqueWordsCount = 0;
        totalWordsCount = 0;
        tweetsCount = 0;
        avgWordCount = 0;

        Tweet currentTweet = new Tweet();//Tweet courant
        List<String> lines;//lignes du fichier courant
        ArrayList<String> words;//mots du fichier courant
        Integer currentLine = 0;//numero de la ligne courante
        Integer progress;//Progression de la fonction en %
        TreeMap<String, Integer> tfMap;//TF Map du tweet courant
        TreeMap<String, Integer>[] tfMaps;//Tableau des tfMap de chaque tweet
        TreeMap<String, Integer> dfMap;//DF Map du fichier
        Integer currentWordTF;//TF du mot courant
        Integer currentWordDF;//DF du mot courant
        Integer corpusCurrentWordTf;//TF courant pour la map corpusTfMap

        //Top & Flop
        Polarity currentPolarity;
        Brand currentBrand;
        Integer currentMapValue;

        //Parcours de chaque ligne
        lines = FileTools.readLinesTextFile(trainPath, normalizer.getCharset());

        //Init
        tweets = new Tweet[lines.size()];
        tfMaps = new TreeMap[lines.size()];
        dfMap = new TreeMap<String, Integer>();
        corpusTfMap = new TreeMap<String, Integer>();
        tfMapsPolarity = new TreeMap[Polarity.values().length];
        tfMapsBrand = new TreeMap[Brand.values().length];
        aPriorisPolarity = new Double[Polarity.values().length];
        aPriorisBrand = new Double[Brand.values().length];

        for (int i = 0; i < Polarity.values().length; i++) {
            tfMapsPolarity[i] = new TreeMap<String, Integer>();
            aPriorisPolarity[i] = 0d;
        }

        for (int i = 0; i < Brand.values().length; i++) {
            tfMapsBrand[i] = new TreeMap<String, Integer>();
            aPriorisBrand[i] = 0d;
        }

        //Parcours des tweets
        for (String line : lines) {
            tweetsCount++;

            //suppression première parenthèse (doit être le premier caractère)
            line = line.substring(1);

            //parse la polarité
            currentPolarity = parsePolarity(line.substring(0, line.indexOf(",")));
            currentTweet.setPolarity(currentPolarity);
            line = line.substring(line.indexOf(",") + 1);//on supprime "polarité,"
            aPriorisPolarity[currentPolarity.ordinal()]++;

            //parse la marque
            currentBrand = parseBrand(line.substring(0, line.indexOf(")")));
            currentTweet.setBrand(currentBrand);
            line = line.substring(line.indexOf(")") + 2);//on supprime "marque) "
            aPriorisBrand[currentBrand.ordinal()]++;

            //Init tfMap de la ligne courante
            tfMap = new TreeMap<String, Integer>();

            //Parcours de chaque mot
            words = normalizer.normalize(line);
            for (String word : words) {

                //Update tfMap & dfMap
                if (tfMap.containsKey(word)) {//Mot deja rencontré sur cette ligne
                    currentWordTF = tfMap.get(word);
                } else {//Mot vu pour la première fois sur la ligne
                    currentWordTF = 0;

                    //Update DF Map
                    currentWordDF = (dfMap.containsKey(word)) ? dfMap.get(word) : 0;
                    dfMap.put(word, currentWordDF + 1);
                }
                tfMap.put(word, currentWordTF + 1);

                //Update corpusTfMap
                corpusCurrentWordTf = (corpusTfMap.containsKey(word)) ? corpusTfMap.get(word) : 0;
                corpusTfMap.put(word, corpusCurrentWordTf + 1);

                //Update TFMaps polarity et Brand
                currentMapValue = (tfMapsPolarity[currentPolarity.ordinal()].containsKey(word)) ? tfMapsPolarity[currentPolarity.ordinal()].get(word) : 0;
                tfMapsPolarity[currentPolarity.ordinal()].put(word, currentMapValue + 1);
                currentMapValue = (tfMapsBrand[currentBrand.ordinal()].containsKey(word)) ? tfMapsBrand[currentBrand.ordinal()].get(word) : 0;
                tfMapsBrand[currentBrand.ordinal()].put(word, currentMapValue + 1);
            }

            //Stock la tfMap
            tfMaps[currentLine] = tfMap;

            tweets[currentLine] = new Tweet(currentTweet);

            currentLine++;
            progress = 100 * currentLine / lines.size();
            publish(progress);//Appel process qui update GUI
            //System.out.println(progress + "%");
        }

        //Update a prioris
        for (int i = 0; i < Polarity.values().length; i++) {
            aPriorisPolarity[i] /= lines.size();
        }

        for (int i = 0; i < Brand.values().length; i++) {
            aPriorisBrand[i] /= lines.size();
        }

        //Toutes les lignes ont été parcourues
        publish(99);//Set la progression a 99%

        //Vocabulaire
        vocabulary = new ArrayList<String>();
        wordID = new TreeMap<String, Integer>();
        Integer uniqueWordIndex = 0;

        //System.out.println("Génération de l'index (et gestion occurences uniques)...");
        for (Map.Entry<String, Integer> e : corpusTfMap.entrySet()) {
            if (normalizer.getFilterOneOccurence() && e.getValue() > 1 || !normalizer.getFilterOneOccurence()) {//Filter mots avec une seule occurence ou ne pas filtrer

                vocabulary.add(e.getKey());
                wordID.put(e.getKey(), uniqueWordIndex);

                uniqueWordIndex++;
                totalWordsCount += e.getValue();
            } else { //dans le cas ou on filtre uniques occurences et qu'il y en a qu'une
                //On supprime le mot des tfMaps
                for (int i = 0; i < lines.size(); i++) {
                    tfMaps[i].remove(e.getKey());
                }

                for (int i = 0; i < Polarity.values().length; i++) {
                    tfMapsPolarity[i].remove(e.getKey());
                    tfMapsBrand[i].remove(e.getKey());
                }
            }
        }
        //System.out.println("...done.");

        //Poids tf-idf les plus importants pour chaque polarité
//        for (int i = 0; i < Polarity.values().length - 1; i++) {
//            printMostImportantWordsFromPolariyClass(i, lines.size(), dfMap, 30);
//        }

        //Création du vecteur TF-IDF pour chaque Tweet
        //System.out.println("Creation de " + lines.size() + " vecteurs TF-IDF...");

        //*********
        //TF IDF Start
        //*********
        AbstractVectorTI currentVector;
        double currentTfIDF;

        for (int i = 0; i < lines.size(); i++) {
            if (vectorType == VectorType.Dense) {//Vecteurs denses

                currentVector = new DenseVectorTI(vocabulary.size());
                //pour chaque mot du vocabulaire
                for (String word : vocabulary) {
                    //si le mot n'est pas dans la tf du tweet, alors tfIdf = 0.
                    if (!tfMaps[i].containsKey(word)) {
                        currentTfIDF = 0d;
                    } else {
                        currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));
                    }

                    currentVector.put(wordID.get(word), currentTfIDF);
                }

                //Normalisation et ajout
                currentVector = currentVector.normalize();
                tweets[i].setVector(currentVector);
            } else if (vectorType == VectorType.Sparse) {//Vecteurs creux

                currentVector = new SparseVectorTI(tfMaps[i].size(), vocabulary.size());
                //Pour chaque mot de la tfMap du tweet
                for (String word : tfMaps[i].keySet()) {
                    //Calcul TF.IDF
                    currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));

                    //wordID.get(word) donne l'index du mot word dans le vecteur
                    currentVector.put(wordID.get(word), currentTfIDF);
                }

                //Normalisation et ajout
                currentVector = currentVector.normalize();
                tweets[i].setVector(currentVector);
            } else {
                break;
            }
        }
        //*********
        //TF IDF End
        //*********

        //Nb Mots uniques
        uniqueWordsCount = vocabulary.size();

        //Nb mots en moyenne par ligne
        avgWordCount = totalWordsCount / lines.size();

        publish(100);//Set la progression a 100%

        //System.out.println("...done.");
        //printDF(dfMap);
    }

//    public void makeStatsConsole() throws IOException {
//        //Stats
//        uniqueWordsCount = 0;
//        totalWordsCount = 0;
//        tweetsCount = 0;
//        avgWordCount = 0;
//
//        Tweet currentTweet = new Tweet();//Tweet courant
//        List<String> lines;//lignes du fichier courant
//        ArrayList<String> words;//mots du fichier courant
//        Integer currentLine = 0;//numero de la ligne courante
//        Integer progress;//Progression de la fonction en %
//        TreeMap<String, Integer> tfMap;//TF Map du tweet courant
//        TreeMap<String, Integer>[] tfMaps;//Tableau des tfMap de chaque tweet
//        TreeMap<String, Integer> dfMap;//DF Map du fichier
//        Integer currentWordTF;//TF du mot courant
//        Integer currentWordDF;//DF du mot courant
//        Integer corpusCurrentWordTf;//TF courant pour la map corpusTfMap
//
//        //Top & Flop
//        Polarity currentPolarity;
//        Brand currentBrand;
//        Integer currentMapValue;
//
//        //Parcours de chaque ligne
//        lines = FileTools.readLinesTextFile(trainPath, normalizer.getCharset());
//
//        //Init
//        tweets = new Tweet[lines.size()];
//        tfMaps = new TreeMap[lines.size()];
//        dfMap = new TreeMap<String, Integer>();
//        corpusTfMap = new TreeMap<String, Integer>();
//        tfMapsPolarity = new TreeMap[Polarity.values().length];//positive, negative, neutral, irrelevant
//        tfMapsBrand = new TreeMap[Brand.values().length];//google, apple, microsoft, twitter
//
//        for (int i = 0; i < Polarity.values().length; i++) {
//            tfMapsPolarity[i] = new TreeMap<String, Integer>();
//        }
//
//        for (int i = 0; i < Brand.values().length; i++) {
//            tfMapsBrand[i] = new TreeMap<String, Integer>();
//        }
//
//        for (String line : lines) {
//            tweetsCount++;
//
//            //suppression première parenthèse (doit être le premier caractère)
//            line = line.substring(1);
//
//            //parse la polarité
//            currentPolarity = parsePolarity(line.substring(0, line.indexOf(",")));
//            currentTweet.setPolarity(currentPolarity);
//            line = line.substring(line.indexOf(",") + 1);//on supprime "polarité,"
//
//            //parse la marque
//            currentBrand = parseBrand(line.substring(0, line.indexOf(")")));
//            currentTweet.setBrand(currentBrand);
//            line = line.substring(line.indexOf(")") + 2);//on supprime "marque) "
//
//            //Init tfMap de la ligne courante
//            tfMap = new TreeMap<String, Integer>();
//
//            //Parcours de chaque mot
//            words = normalizer.normalize(line);
//            for (String word : words) {
//
//                //Update tfMap & dfMap
//                if (tfMap.containsKey(word)) {//Mot deja rencontré sur cette ligne
//                    currentWordTF = tfMap.get(word);
//                } else {//Mot vu pour la première fois sur la ligne
//                    currentWordTF = 0;
//
//                    //Update DF Map
//                    currentWordDF = (dfMap.containsKey(word)) ? dfMap.get(word) : 0;
//                    dfMap.put(word, currentWordDF + 1);
//                }
//                tfMap.put(word, currentWordTF + 1);
//
//                //Update corpusTfMap
//                corpusCurrentWordTf = (corpusTfMap.containsKey(word)) ? corpusTfMap.get(word) : 0;
//                corpusTfMap.put(word, corpusCurrentWordTf + 1);
//
//                //Update TFMaps polarity et Brand
//                currentMapValue = (tfMapsPolarity[currentPolarity.ordinal()].containsKey(word)) ? tfMapsPolarity[currentPolarity.ordinal()].get(word) : 0;
//                tfMapsPolarity[currentPolarity.ordinal()].put(word, currentMapValue + 1);
//                currentMapValue = (tfMapsBrand[currentBrand.ordinal()].containsKey(word)) ? tfMapsBrand[currentBrand.ordinal()].get(word) : 0;
//                tfMapsBrand[currentBrand.ordinal()].put(word, currentMapValue + 1);
//            }
//
//            //Stock la tfMap
//            tfMaps[currentLine] = tfMap;
//
//            tweets[currentLine] = new Tweet(currentTweet);
//
//            currentLine++;
//            //System.out.println(progress + "%");
//        }
//
//
//        //Vocabulaire
//        vocabulary = new ArrayList<String>();
//        wordID = new TreeMap<String, Integer>();
//        Integer uniqueWordIndex = 0;
//
//        for (Map.Entry<String, Integer> e : corpusTfMap.entrySet()) {
//            //Filter mots avec une seule occurence
//            if (normalizer.getFilterOneOccurence() && e.getValue() > 1 || !normalizer.getFilterOneOccurence()) {
//
//                vocabulary.add(e.getKey());
//                wordID.put(e.getKey(), uniqueWordIndex);
//
//                uniqueWordIndex++;
//                totalWordsCount += e.getValue();
//            } else {
//                //On supprime le mot des tfMaps
//                for (int i = 0; i < lines.size(); i++) {
//                    tfMaps[i].remove(e.getKey());
//                }
//            }
//        }
//
//        //Création du vecteur TF-IDF pour chaque Tweet
//        System.out.println("Creation de " + lines.size() + " vecteurs TF-IDF...");
//
//        //*********
//        //TF IDF Start
//        //*********
//        AbstractVectorTI currentVector;
//        double currentTfIDF;
//
//        for (int i = 0; i < lines.size(); i++) {
//            if (vectorType == VectorType.Dense) {//Vecteurs denses
//
//                currentVector = new DenseVectorTI(vocabulary.size());
//                //pour chaque mot du vocabulaire
//                for (String word : vocabulary) {
//                    //si le mot n'est pas dans la tf du tweet, alors tfIdf = 0.
//                    if (!tfMaps[i].containsKey(word)) {
//                        currentTfIDF = 0d;
//                    } else {
//                        currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));
//                    }
//
//                    currentVector.put(wordID.get(word), currentTfIDF);
//                }
//            } else {//Vecteurs creux
//
//                currentVector = new SparseVectorTI(tfMaps[i].size(), vocabulary.size());
//                //Pour chaque mot de la tfMap du tweet
//                for (String word : tfMaps[i].keySet()) {
//                    //Calcul TF.IDF
//                    currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));
//
//                    //wordID.get(word) donne l'index du mot word dans le vecteur
//                    currentVector.put(wordID.get(word), currentTfIDF);
//                }
//            }
//
//            //Normalisation et ajout
//            currentVector = currentVector.normalize();
//            tweets[i].setVector(currentVector);
//        }
//        //*********
//        //TF IDF End
//        //*********
//
//        //Nb Mots uniques
//        uniqueWordsCount = vocabulary.size();
//
//        //Nb mots en moyenne par ligne
//        avgWordCount = totalWordsCount / lines.size();
//        System.out.println("...done.");
//        //printDF(dfMap);
//    }

    //Affiche la map document frequency (test)
    public void printDF(Map<String, Integer> dfMap) {
        String print = "";
        Integer entryPerLine = 50;
        Integer counter = 0;

        for (Map.Entry<String, Integer> e : dfMap.entrySet()) {
            //if (e.getValue() == 1) {//Une seule occurence
            print += e.getKey() + ": " + e.getValue() + ",  ";
            counter++;

            if (counter % entryPerLine == 0) {
                print += "\n";
            }
            //}
        }

        System.out.println(print);
    }

    public String resume() {
        String str = normalizer.toString();

        return str;
    }
    
    //tests
    private void printMostImportantWordsFromPolariyClass(Integer classId, Integer lineCount, TreeMap<String, Integer> dfMap, Integer wordCount) {

        System.out.println("");
        System.out.println("Poids les plus importants pour " + Polarity.values()[classId] + " : ");
        TreeMap<String, Double> tfidfPolarityPositive = new TreeMap<String, Double>();
        Double tfIdf;
        for (Map.Entry<String, Integer> e : tfMapsPolarity[classId].entrySet()) {
            if (!dfMap.containsKey(e.getKey())) {
                tfIdf = 0.;
            } else {
                tfIdf = (double) e.getValue() * Math.log10((double) lineCount / dfMap.get(e.getKey()));
            }
            tfidfPolarityPositive.put(e.getKey(), tfIdf);
        }

        Integer counter = 0;
        TreeMap<String, Double> orderedMap = MapTools.getSortedMapDouble(tfidfPolarityPositive);
        for (Map.Entry<String, Double> e : orderedMap.entrySet()) {
            System.out.println(e.getKey() + " (" + e.getValue() + ")");
            counter++;

            if (counter > wordCount) {
                break;
            }
        }
    }

    @Override
    protected Void doInBackground() throws Exception {
        makeStats();

        return null;
    }

    @Override
    protected void process(List<Integer> values) {
        for (Integer val : values) {
            if(gui != null) gui.updateBarStats(val);
        }
    }

    @Override
    protected void done() {
        if(gui != null) gui.statsFilled();
    }
}
